/*

  json_array.cpp
  Shan.Lib

 Copyright (c) 2013, Sung-Han Park
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 1. Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#include "json_array.h"
#include "json_object.h"
#include "json_parser.h"

// object
void shan::json::array::add(const shan::json::object& val) {
	push_back(std::make_shared<shan::json::object>(val));
}

void shan::json::array::add(shan::json::object&& val) {
	push_back(std::make_shared<shan::json::object>(std::move(val)));
}

void shan::json::array::add(const shan::json::array& val) {
	push_back(std::make_shared<shan::json::array>(val));
}

void shan::json::array::add(shan::json::array&& val) {
	push_back(std::make_shared<shan::json::array>(std::move(val)));
}

void shan::json::array::add(const shan::json::string& val) {
	push_back(std::make_shared<shan::json::string>(val));
}

void shan::json::array::add(shan::json::string&& val) {
	push_back(std::make_shared<shan::json::string>(std::move(val)));
}

void shan::json::array::add(const shan::json::number val) {
	push_back(std::make_shared<shan::json::number>(val));
}

void shan::json::array::add(const shan::json::true_value val) {
	push_back(std::make_shared<shan::json::true_value>());
}

void shan::json::array::add(const shan::json::false_value val) {
	push_back(std::make_shared<shan::json::false_value>());
}

void shan::json::array::add(const shan::json::null_value val) {
	push_back(std::make_shared<shan::json::null_value>());
}

void shan::json::array::add(const std::string& val) {
	push_back(std::make_shared<string>(val));
}

void shan::json::array::add(std::string&& val) {
	push_back(std::make_shared<string>(std::move(val)));
}

void shan::json::array::add(const char* val) {
	push_back(std::make_shared<string>(val));
}

void shan::json::array::add(char val) {
	push_back(std::make_shared<number>((long long)val));
}

void shan::json::array::add(unsigned char val) {
	push_back(std::make_shared<number>((long long)val));
}

void shan::json::array::add(short val) {
	push_back(std::make_shared<number>((long long)val));
}

void shan::json::array::add(unsigned short val) {
	push_back(std::make_shared<number>((long long)val));
}

void shan::json::array::add(int val) {
	push_back(std::make_shared<number>((long long)val));
}

void shan::json::array::add(unsigned int val) {
	push_back(std::make_shared<number>((long long)val));
}

void shan::json::array::add(long val) {
	push_back(std::make_shared<number>((long long)val));
}

void shan::json::array::add(unsigned long val) {
	push_back(std::make_shared<number>((long long)val));
}

void shan::json::array::add(long long val) {
	push_back(std::make_shared<number>(val));
}

void shan::json::array::add(unsigned long long val) {
	push_back(std::make_shared<number>((long long)val)); // will lose some data
}

void shan::json::array::add(float val) {
	push_back(std::make_shared<number>((double)val));
}

void shan::json::array::add(double val) {
	push_back(std::make_shared<number>(val));
}

void shan::json::array::add(bool val) {
	val ?
	push_back(std::make_shared<true_value>()) :
	push_back(std::make_shared<false_value>());
}

void shan::json::array::add(std::nullptr_t val) {
	push_back(std::make_shared<null_value>());
}

std::shared_ptr<shan::json::object> shan::json::array::get_object(std::size_t inx, std::shared_ptr<shan::json::object> defValue) {
	try {
		if (typeid(*base_type::at(inx)) == typeid(json::object))
			return std::dynamic_pointer_cast<json::object>(at(inx));
		else
			throw json::exception("json::array[" + std::to_string(inx) + "] is not compatible with json::object.");
	} catch (const std::out_of_range&) {
		return defValue;
	} catch (const std::exception& e) {
		throw json::exception(e.what());
	}
}

std::shared_ptr<shan::json::array> shan::json::array::get_array(std::size_t inx, std::shared_ptr<shan::json::array> defValue) {
	try {
		if (typeid(*base_type::at(inx)) == typeid(json::array))
			return std::dynamic_pointer_cast<json::array>(at(inx));
		else
			throw json::exception("json::array[" + std::to_string(inx) + "] is not compatible with json::array.");
	} catch (const std::out_of_range&) {
		return defValue;
	} catch (const std::exception& e) {
		throw json::exception(e.what());
	}
}

std::string shan::json::array::to_json() const {
	std::ostringstream ret;
	ret << "[";

	auto it = begin();
	if (it != end()) {
		ret << (*it)->to_json();
		for (it++ ; it != end() ; it++)
			ret << "," << (*it)->to_json();
	}
	ret << "]";
	return ret.str();
}

void shan::json::array::internal_parse(std::istream& is) {
	if (is.eof() || (is.peek() != '['))
		throw json::exception("Invalid syntax was found.");
	is.get();

	while (!is.eof()) {
		shan::json::parser::skip_white(is);

		value_ptr value;
		switch (is.peek()) {
			case '{':
				value = std::make_shared<json::object>();
				break;
			case '[':
				value = std::make_shared<json::array>();
				break;
			case '"':
				value = std::make_shared<json::string>();
				break;
			case 't':
				value = std::make_shared<json::true_value>();
				break;
			case 'f':
				value = std::make_shared<json::false_value>();
				break;
			case 'n':
				value = std::make_shared<json::null_value>();
				break;
			default:
				value = std::make_shared<json::number>();
				break;
		}
		std::dynamic_pointer_cast<json::value>(value)->internal_parse(is);

		push_back(std::move(value)); // value will not use any more, so moved.

		shan::json::parser::skip_white(is);

		if (is.eof())
			throw json::exception("Invalid syntax was found. Unexpected EOF was found during parsing json::array.");

		if (is.peek() == ',') {
			is.get(); // skip ','
			continue;
		}
		else if (is.peek() == ']') {
			is.get(); // skip '}'
			return;
		}
		else
			throw json::exception("Invalid syntax was found. ',' or ']' was expected.");
	}
}
